package com.sz.biz.logistics.serialno.utils;

import com.sz.biz.logistics.serialno.constants.BillNumberConstants;
import com.sz.common.base.utils.StringUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * Package: com.sz.biz.logistics.channel.utils<br>
 * Description: 从模式串生成单号<br>
 * @author WuHan<br>
 * @date 2017-11-27 20:15<br>
 * @serial
 * @since sys-1.1.3.1
 */
public class PatternStringToNumber {
    
    /**
     * 带有分隔符的pattern长度是包含两个正常单号长度的
     */
    public static final int DOUBLE_SIZE = 2;
    public static final String STEP = "step";
    public static final String NEXT_NUMBER = "nextNumber";
    
    /**
     * 从模式串计算出当前单号的序号
     * @param input 实际单号
     * @param pattern 模式串，由常规数字/字母的固定位和"*"代表的可变数字、"#"代表的可变字母及"!"代表的可变数字/字母组成<br>
     * @return 实际单号的序号
     */
    private static long getNumberByPlaceHolder(String input, String pattern) {
        long count = 0;
        String subPattern;
        
        for (int i = 0; i < pattern.length(); i++) {
            subPattern = pattern.substring(i, i + 1);
            if (subPattern.equals(BillNumberConstants.Symbols.SYMBOL_DIGIT)) {
                count = count * BillNumberConstants.PatternParse.NUMBER_BASE + (BillNumberConstants.PatternParse.CHARACTER_INTEGER_MAP.get(input.charAt(i)) - BillNumberConstants.PatternParse.CHARACTER_INTEGER_MAP.get('0'));
            }
            
            if (subPattern.equals(BillNumberConstants.Symbols.SYMBOL_LETTER)) {
                count = count * BillNumberConstants.PatternParse.LETTER_BASE  + (BillNumberConstants.PatternParse.CHARACTER_INTEGER_MAP.get(input.charAt(i)) - BillNumberConstants.PatternParse.CHARACTER_INTEGER_MAP.get('A'));
            }
            
            if (subPattern.equals(BillNumberConstants.Symbols.SYMBOL_MIXED)) {
                count = count * BillNumberConstants.PatternParse.MIXCHAR_BASE  + (BillNumberConstants.PatternParse.CHARACTER_INTEGER_MAP.get(input.charAt(i)) - BillNumberConstants.PatternParse.CHARACTER_INTEGER_MAP.get('0'));
            }
        }
        count++;
        return count;
    }


    public static String generateChannelNumber(String currentIndex , String prefix , String serialNumber , String postfix , long stepLength) {
        int serialNumberLength = 0;
        int prefixLength =0;
        int postfixLength = 0;
        if(StringUtils.isNotEmpty(prefix)){
            prefixLength = prefix.length();
        }
        if(StringUtils.isNotEmpty(serialNumber)){
            serialNumberLength  = serialNumber.length();
        }
        if(StringUtils.isNotEmpty(postfix)){
            postfixLength = prefix.length();
        }
        if(serialNumber.contains(BillNumberConstants.Symbols.SYMBOL_SEPERATOR)){
            serialNumberLength =serialNumber.split("-")[0].length();
        }

        String currentIndexPrefix = currentIndex.substring(0 , prefixLength);
        String currentIndexSerial = currentIndex.substring(prefixLength , serialNumberLength + prefixLength);
        String currentIndexPostFix = currentIndex.substring(serialNumberLength + prefixLength , currentIndex.length());
        if (StringUtils.isNotEmpty(postfix)) {
            Map<String,Object> postResult = getStepNumber(postfix , currentIndexPostFix , stepLength);
            if (postResult != null) {
                currentIndexPostFix = (String) postResult.get(NEXT_NUMBER);
                long postStep = (long) postResult.get(STEP);
                if(StringUtils.isNotEmpty(serialNumber)){
                    if(postStep!=0){
                        Map<String,Object> serialResult = getStepNumber(serialNumber , currentIndexSerial , postStep);
                        if (serialResult != null) {
                            currentIndexSerial = (String) serialResult.get(NEXT_NUMBER);
                            long serialStep = (long) serialResult.get(STEP);
                            if (StringUtils.isNotEmpty(prefix)) {
                                if(serialStep != 0){
                                    Map<String,Object> prefixResult = getStepNumber(prefix , currentIndexPrefix , serialStep);
                                    if (prefixResult != null) {
                                        currentIndexPrefix = (String) prefixResult.get(NEXT_NUMBER);
                                        long prefixStep = (long) prefixResult.get(STEP);
                                        if(prefixStep!=0){
                                            //越界
                                            return currentIndexPrefix + currentIndexSerial + currentIndexPostFix+1;
                                        }
                                    }
                                }
                            }else{
                                if(serialStep!=0){
                                    return currentIndexPrefix + currentIndexSerial + currentIndexPostFix+1;
                                }
                            }

                        }
                    }
                }else{
                    if(postStep!=0){
                        //越界
                        return currentIndexPrefix + currentIndexSerial + currentIndexPostFix+1;
                    }
                }
            }
        }else{
            if(StringUtils.isNotEmpty(serialNumber)){
                Map<String,Object> serialResult = getStepNumber(serialNumber , currentIndexSerial , stepLength);
                if (serialResult != null) {
                    currentIndexSerial = (String) serialResult.get(NEXT_NUMBER);
                    long serialStep = (long) serialResult.get(STEP);
                    if (serialStep != 0 && StringUtils.isNotEmpty(prefix)) {
                        Map<String,Object> prefixResult = getStepNumber(prefix , currentIndexPrefix , serialStep);
                        if (prefixResult != null) {
                            currentIndexPrefix = (String) prefixResult.get(NEXT_NUMBER);
                            long prefixStep = (long) prefixResult.get(STEP);
                            if(prefixStep!=0){
                                //越界
                             return currentIndexPrefix + currentIndexSerial + currentIndexPostFix+1;
                            }
                        }
                    }
                    if(StringUtils.isEmpty(prefix)){
                        if(serialStep!=0){
                            return currentIndexPrefix + currentIndexSerial + currentIndexPostFix+1;
                        }
                    }
                }
            }
        }
        return currentIndexPrefix + currentIndexSerial + currentIndexPostFix;
    }
    

    public static Map<String,Object> getStepNumber(String format, String current,long stepLength){
        if(format.contains(BillNumberConstants.Symbols.SYMBOL_VERIFYCODE)&&!current.contains(BillNumberConstants.Symbols.SYMBOL_VERIFYCODE)){
             current = new StringBuilder(current).replace(format.indexOf(BillNumberConstants.Symbols.SYMBOL_VERIFYCODE),format.indexOf(BillNumberConstants.Symbols.SYMBOL_VERIFYCODE)+1,BillNumberConstants.Symbols.SYMBOL_VERIFYCODE).toString();
        }
        Map<String,Object> result = new HashMap<>(1);
        if(!format.contains(
            BillNumberConstants.Symbols.SYMBOL_LETTER) && !format.contains(
            BillNumberConstants.Symbols.SYMBOL_DIGIT) && !format.contains(
            BillNumberConstants.Symbols.SYMBOL_MIXED)&& !format.contains(BillNumberConstants.Symbols.SYMBOL_SEPERATOR)){
            result.put(NEXT_NUMBER, current);
            result.put(STEP, stepLength);
            return result;
        }else{
            if(format.contains(BillNumberConstants.Symbols.SYMBOL_SEPERATOR)){
                String startFormat = format.split(BillNumberConstants.Symbols.SYMBOL_SEPERATOR)[0];
                String endFormat = format.split(BillNumberConstants.Symbols.SYMBOL_SEPERATOR)[1];
                int digit = 0;
                long lowerTemp = 0;
                long upperTemp = 0;
                long temp = 0;
                StringBuilder stringBuilder = new StringBuilder();
                if(startFormat.contains(BillNumberConstants.Symbols.SYMBOL_VERIFYCODE)){
                     lowerTemp = Long.valueOf(new StringBuilder(startFormat).deleteCharAt(format.indexOf
                            (BillNumberConstants.Symbols.SYMBOL_VERIFYCODE)).toString());
                     upperTemp = Long.valueOf(new StringBuilder(endFormat).deleteCharAt(format.indexOf(BillNumberConstants.Symbols.SYMBOL_VERIFYCODE)).toString());
                     temp = Long.valueOf(new StringBuilder(current).deleteCharAt(format.indexOf(BillNumberConstants.Symbols.SYMBOL_VERIFYCODE)).toString());
                }else {
                     lowerTemp = Long.valueOf(new StringBuilder(startFormat).toString());
                     upperTemp = Long.valueOf(new StringBuilder(endFormat).toString());
                     temp = Long.valueOf(new StringBuilder(current).toString());
                }
                long gap = upperTemp - lowerTemp + 1;
                long diff = temp - lowerTemp;
                long currentNumber = lowerTemp + (stepLength + digit + diff) % gap;
                stepLength = (stepLength + digit + diff) / gap;
                for (int i = current.length() - 1; i >= 0; i--) {
                    if (current.substring(i, i + 1).equals(BillNumberConstants.Symbols.SYMBOL_VERIFYCODE)) {
                        stringBuilder.insert(0, BillNumberConstants.Symbols.SYMBOL_VERIFYCODE);
                    } else {
                        stringBuilder.insert(0, currentNumber % 10);
                        currentNumber /= 10;
                    }
                }
                result.put(NEXT_NUMBER, stringBuilder.toString());
                result.put(STEP, stepLength);
                return result;
            }
            else{
                int digit = 0;
                int content;
                StringBuilder temp = new StringBuilder();
                for(int i=current.length()-1;i>=0;i--){
                    switch(format.substring(i,i+1)){
                        case BillNumberConstants.Symbols.SYMBOL_DIGIT:
                            content = BillNumberConstants.PatternParse.CHARACTER_INTEGER_MAP.get(current.charAt(i)) - BillNumberConstants.PatternParse.CHARACTER_INTEGER_MAP.get('0') + Math.toIntExact(stepLength % BillNumberConstants.PatternParse.NUMBER_BASE) + digit;
                            temp.insert(0, BillNumberConstants.PatternParse.CHARACTER_INTEGER_ARRAY[content % BillNumberConstants.PatternParse.NUMBER_BASE]);
                            digit = BillNumberConstants.PatternParse.NUMBER_BASE > content ? 0 : 1;
                            stepLength /= BillNumberConstants.PatternParse.NUMBER_BASE;
                            break;

                        case BillNumberConstants.Symbols.SYMBOL_LETTER:
                            content = BillNumberConstants.PatternParse.CHARACTER_INTEGER_MAP.get(current.charAt(i)) - BillNumberConstants.PatternParse.CHARACTER_INTEGER_MAP.get('A') + Math.toIntExact(stepLength % BillNumberConstants.PatternParse.LETTER_BASE) + digit;
                            temp.insert(0, BillNumberConstants.PatternParse.CHARACTER_INTEGER_ARRAY[content % BillNumberConstants.PatternParse.LETTER_BASE + BillNumberConstants.PatternParse.NUMBER_BASE]);
                            digit = BillNumberConstants.PatternParse.LETTER_BASE > content ? 0 : 1;
                            stepLength /= BillNumberConstants.PatternParse.LETTER_BASE;
                            break;

                        case BillNumberConstants.Symbols.SYMBOL_MIXED:
                            content = BillNumberConstants.PatternParse.CHARACTER_INTEGER_MAP.get(current.charAt(i)) - BillNumberConstants.PatternParse.CHARACTER_INTEGER_MAP.get('0') + Math.toIntExact(stepLength % BillNumberConstants.PatternParse.MIXCHAR_BASE) + digit;
                            temp.insert(0, BillNumberConstants.PatternParse.CHARACTER_INTEGER_ARRAY[content % BillNumberConstants.PatternParse.MIXCHAR_BASE]);
                            digit = BillNumberConstants.PatternParse.MIXCHAR_BASE > content ? 0 : 1;
                            stepLength /= BillNumberConstants.PatternParse.MIXCHAR_BASE;
                            break;
                        case BillNumberConstants.Symbols.SYMBOL_VERIFYCODE:
                            temp.insert(0, BillNumberConstants.Symbols.SYMBOL_VERIFYCODE);
                            break;
                        default:
                            temp.insert(0,current.charAt(i));
                            break;
                    }
                }
                result.put(NEXT_NUMBER, temp.toString());
                result.put(STEP, stepLength+digit);
                return result;
            }
        }
    }
}
